// import { useLocation } from 'react-router-dom';
import CryptoJS from 'crypto-js';
import { Base64 } from 'js-base64';
 
const APPID = 'd1245a6d';
const API_SECRET = 'ZDYzNmMwOTU5NmQyZjQ0ZjYzMjJkNWMw';
const API_KEY = 'cc962e6086daf99eca528367931cae2b';
 
// 获取讯飞语音权限签名
function getWebSocketUrl(apiKey, apiSecret) {
  var url = 'wss://tts-api.xfyun.cn/v2/tts';
  // var host = window.location.host;
  var host = '127.0.0.1:5500';
 
  var date = new Date().toGMTString();
  var algorithm = 'hmac-sha256';
  var headers = 'host date request-line';
  var signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v2/tts HTTP/1.1`;
  var signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret);
  var signature = CryptoJS.enc.Base64.stringify(signatureSha);
 
  var authorizationOrigin = `api_key="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`;
  var authorization = btoa(authorizationOrigin);
  url = `${url}?authorization=${authorization}&date=${date}&host=${host}`;
  return url;
}
 
function encodeText(text, type) {
  if (type === 'unicode') {
    let buf = new ArrayBuffer(text.length * 4);
    let bufView = new Uint16Array(buf);
    for (let i = 0, strlen = text.length; i < strlen; i++) {
      bufView[i] = text.charCodeAt(i);
    }
    let binary = '';
    let bytes = new Uint8Array(buf);
    let len = bytes.byteLength;
    for (let i = 0; i < len; i++) {
      binary += String.fromCharCode(bytes[i]);
    }
    return window.btoa(binary);
  } else {
    return Base64.encode(text);
  }
}
 
/**
 * WebSocket: 传入文本解析出语音mp3
 * @param textarea：文本内容
 * @param configs：语音参数
 * @param callback: 回调函数，返回url播放链接
 */
 
let ttsWS,
  audioData = [];
export const connectWebSocket = (textarea = '', callback, configs = {}) => {
  audioData = [];
  const url = getWebSocketUrl(API_KEY, API_SECRET);
 
  if ('WebSocket' in window) {
    ttsWS = new WebSocket(url);
  } else {
    alert('浏览器不支持WebSocket');
    return;
  }
 
  ttsWS.onopen = (e) => {
    var text = textarea || document.getElementById('textarea').value.trim() || '请输入您要合成的文本';
    var tte = 'UTF8';
    var params = {
      common: {
        app_id: APPID,
      },
      // 配置文档： https://www.xfyun.cn/doc/tts/online_tts/API.html#%E6%8E%A5%E5%8F%A3%E8%B0%83%E7%94%A8%E6%B5%81%E7%A8%8B
      business: {
        aue: 'lame',
        sfl: 1,
        // aue: 'raw',
        auf: 'audio/L16;rate=16000',
        bgs: 1,
        volume: 100,
        tte,
        ...configs,
      },
      data: {
        status: 2,
        text: encodeText(text, tte),
      },
    };
    ttsWS.send(JSON.stringify(params));
  };
 
  ttsWS.onmessage = (e) => {
    let jsonData = JSON.parse(e.data);
    // 合成失败
    if (jsonData.code !== 0) {
      console.error('语音合成失败===', jsonData);
      return;
    } else {
      // console.log('语音合成success===', jsonData);
      audioData.push(jsonData.data.audio);
    }
 
    if (jsonData.code === 0 && jsonData.data.status === 2) {
      ttsWS.close();
      // audio.src = 'data:audio/wav;base64,' + jsonData.data.audio;
 
      decodeAndConcatAudioFiles(audioData).then((url) => {
        callback?.(url);
      });
    }
    ttsWS.onerror = (e) => {
      console.error('WebSocket链接失败===', e);
    };
    ttsWS.onclose = (e) => {};
  };
};
 
// 直接调用有道API语音在线合成
export const textToSpeech = (textarea = '', callback) => {
  var text = textarea || document.getElementById('textarea').value.trim() || '请输入您要合成的文本';
  // audio.src = "http://tts.baidu.com/text2audio?lan=zh&ie=UTF-8&text='" + encodeURI(textarea); //失效
  var url = `https://dict.youdao.com/dictvoice?audio=${encodeURI(text)}&le=zh`;
  callback?.(url);
};
 
// 创建AudioContext对象
const AudioContext = window.AudioContext || window.webkitAudioContext;
const audioContext = new AudioContext();
audioContext.resume();
 
/**
 * 解码并拼接音频文件
 * audioFiles: 需要拼接的音频文件的base64格式数据集合
 */
async function decodeAndConcatAudioFiles(audioFiles) {
  const audioBuffers = []; // 存储解码后的音频数据的数组
 
  for (let i = 0; i < audioFiles.length; i++) {
    const base64Data = audioFiles[i];
 
    // 将base64数据解码为ArrayBuffer格式
    const arrayBuffer = Uint8Array.from(atob(base64Data), (c) => c.charCodeAt(0)).buffer;
 
    // 解码ArrayBuffer格式的音频数据
    const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
 
    // 将解码后的音频数据存储到数组中
    audioBuffers.push(audioBuffer);
  }
 
  const concatAudioBuffer = concatAudio(audioBuffers); //将多个buffer音频片段拼接成一个完整音频
  const newAudioSrc = URL.createObjectURL(bufferToWave(concatAudioBuffer, concatAudioBuffer.length));
 
  return newAudioSrc; //生成最终audio播放地址
}
 
// 拼接音频的方法,参考文档：https://www.zhangxinxu.com/wordpress/2023/10/js-audio-audiobuffer-concat-merge/
export const concatAudio = (arrBufferList) => {
  // 获得 AudioBuffer
  const audioBufferList = arrBufferList;
  // 最大通道数
  const maxChannelNumber = Math.max(...audioBufferList.map((audioBuffer) => audioBuffer.numberOfChannels));
  // 总长度
  const totalLength = audioBufferList.map((buffer) => buffer.length).reduce((lenA, lenB) => lenA + lenB, 0);
 
  // 创建一个新的 AudioBuffer
  const newAudioBuffer = audioContext.createBuffer(maxChannelNumber, totalLength, audioBufferList[0].sampleRate);
  // 将所有的 AudioBuffer 的数据拷贝到新的 AudioBuffer 中
  let offset = 0;
 
  audioBufferList.forEach((audioBuffer, index) => {
    for (let channel = 0; channel < audioBuffer.numberOfChannels; channel++) {
      newAudioBuffer.getChannelData(channel).set(audioBuffer.getChannelData(channel), offset);
    }
 
    offset += audioBuffer.length;
  });
 
  return newAudioBuffer;
};
 
// AudioBuffer 转 blob
function bufferToWave(abuffer, len) {
  var numOfChan = abuffer.numberOfChannels,
    length = len * numOfChan * 2 + 44,
    buffer = new ArrayBuffer(length),
    view = new DataView(buffer),
    channels = [],
    i,
    sample,
    offset = 0,
    pos = 0;
 
  // write WAVE header
  // "RIFF"
  setUint32(0x46464952);
  // file length - 8
  setUint32(length - 8);
  // "WAVE"
  setUint32(0x45564157);
  // "fmt " chunk
  setUint32(0x20746d66);
  // length = 16
  setUint32(16);
  // PCM (uncompressed)
  setUint16(1);
  setUint16(numOfChan);
  setUint32(abuffer.sampleRate);
  // avg. bytes/sec
  setUint32(abuffer.sampleRate * 2 * numOfChan);
  // block-align
  setUint16(numOfChan * 2);
  // 16-bit (hardcoded in this demo)
  setUint16(16);
  // "data" - chunk
  setUint32(0x61746164);
  // chunk length
  setUint32(length - pos - 4);
 
  // write interleaved data
  for (i = 0; i < abuffer.numberOfChannels; i++) channels.push(abuffer.getChannelData(i));
 
  while (pos < length) {
    // interleave channels
    for (i = 0; i < numOfChan; i++) {
      // clamp
      sample = Math.max(-1, Math.min(1, channels[i][offset]));
      // scale to 16-bit signed int
      sample = (0.5 + sample < 0 ? sample * 32768 : sample * 32767) | 0;
      // write 16-bit sample
      view.setInt16(pos, sample, true);
      pos += 2;
    }
    // next source sample
    offset++;
  }
 
  // create Blob
  return new Blob([buffer], { type: 'audio/wav' });

  function setUint16(data) {
    view.setUint16(pos, data, true);
    pos += 2;
  }
 
  function setUint32(data) {
    view.setUint32(pos, data, true);
    pos += 4;
  }
}